﻿using SDL2;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using Color = SDL2.SDL.SDL_Color;
using Rect = SDL2.SDL.SDL_Rect;
namespace BITUI.Tile
{

    class BankSlot
    {
        public IntPtr sdlTexture;
        public int texWidth;
        public int texHeight;
        public int tilePutX;
        public int tilePutY;
        public int LineHeight;
    }
    public class Bank
    {
        const int defaultslottextsize = 1024;
        //1024的贴图可以放得下 16384 个Tile（一个Tile8x8）,一万多，足够浪费了
        //注：Nes 才支持512个Tile

        //我们只需要把文字分到一个单独的Bank里
        //文字默认用的12x12，一张1024贴图能放 7,225‬ 个，这个数量比区位码保存的汉字多一些，这时候，我们只需要启动一个动态字模机制，
        //绝对用不到全部7225个文字,也就是放不满
        internal Bank(Color[] palette, int TransparentColorIndex, IntPtr sdlRender)
        {
            this.palette = (new List<Color>(palette)).AsReadOnly();
            this.TransparentColorIndex = TransparentColorIndex;
            this.sdlRender = sdlRender;

            this.slots = new List<BankSlot>();
            this.namedTile = new Dictionary<string, RenderTile>();
            this.namedTileData = new Dictionary<string, TileData>();

            this.CreateSlot(defaultslottextsize, defaultslottextsize);

        }
        IntPtr sdlRender;
        List<BankSlot> slots;
        void CreateSlot(int width, int height)
        {
            BankSlot slot = new BankSlot();
            slot.texWidth = width;
            slot.texHeight = height;
            slot.tilePutX = 0;
            slot.tilePutY = 0;
            slot.LineHeight = 0;
            //SDL裏面都是高位算 ABGR  實際上是別人的RGBA
            slot.sdlTexture = SDL.SDL_CreateTexture(this.sdlRender, SDL.SDL_PIXELFORMAT_ARGB8888,
            (int)SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_STREAMING, width, height);
            //SDL_TEXTUREACCESS_STATIC 贴图适合一次创建，不再改动，我们需要经常修改，用streaming

            SDL.SDL_SetTextureBlendMode(slot.sdlTexture, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);

            this.slots.Add(slot);

        }
        public int bankID
        {
            get;
            internal set;
        }
        public IReadOnlyList<Color> palette
        {
            get;
            private set;
        }
        public int TransparentColorIndex
        {
            get;
            private set;
        }
        Dictionary<string, RenderTile> namedTile;

        Dictionary<string, TileData> namedTileData;

        private Tile.RenderTile _CreateTile(string name, TileData data, TilePaletteData palettedata)
        {
            if (namedTile.TryGetValue(name, out RenderTile rt))
            {
                return rt;
            }
            var lastslot = slots[slots.Count - 1];

            //摆不下，换行
            if (lastslot.tilePutX + data.pixelwidth > lastslot.texWidth)
            {
                lastslot.tilePutY += lastslot.LineHeight;
                lastslot.tilePutX = 0;
                lastslot.LineHeight = data.pixelheight;
            }
            //换行也摆不下，新建slot
            if (lastslot.tilePutY + data.pixelheight > lastslot.texHeight)
            {
                this.CreateSlot(defaultslottextsize, defaultslottextsize);
                lastslot = slots[slots.Count - 1];
            }


            var rect = new Rect() { x = lastslot.tilePutX, y = lastslot.tilePutY, w = data.pixelwidth, h = data.pixelheight };
            int lockresult = SDL.SDL_LockTexture(lastslot.sdlTexture, ref rect, out IntPtr pixels, out int pitch);
            unsafe
            {
                byte* pixeldata = (byte*)pixels;
                for (var y = 0; y < data.pixelheight; y++)
                {
                    var line = data.GetLineWithPalette(y, palettedata);
                    for (var x = 0; x < data.pixelwidth; x++)
                    {

                        var color = this.palette[line[x]];
                        pixeldata[y * pitch + x * 4 + 0] = color.b;
                        pixeldata[y * pitch + x * 4 + 1] = color.g;
                        pixeldata[y * pitch + x * 4 + 2] = color.r;
                        pixeldata[y * pitch + x * 4 + 3] = color.a; // pp red

                    }
                }
            }

            SDL.SDL_UnlockTexture(lastslot.sdlTexture);

            lastslot.LineHeight = Math.Max(lastslot.LineHeight, data.pixelheight);
            lastslot.tilePutX += data.pixelwidth;

            var tile = new RenderTile();
            tile.bank = this;
            tile.tex = lastslot.sdlTexture;
            tile.srcrect = rect;
            tile.tileID = namedTile.Count;
            namedTile[name] = tile;
            return tile;
        }

        public Tile.RenderTile CreateTile(string name, TileData data)
        {
            return _CreateTile(name, data, null);
        }
        //也可以先RegData,这样不会生成，GetTileByName时如果找不到，会尝试创建
        public void RegTileData(string name, TileData data)
        {
            namedTileData[name] = data;
        }
        public TileData GetTileData(string name)
        {
            if (namedTileData.TryGetValue(name, out TileData data))
            {
                return data;
            }
            return null;
        }
        //创建一个Tile的不同调色板副本
        public Tile.RenderTile CreateTile2PFromRegTile(string name2p, string srcTileName, TilePaletteData palettedata)
        {
            if (palettedata == null)
            {
                throw new Exception("create 2p tile must have a pallettedata");
            }
            var tiledata = namedTileData[srcTileName];
            return _CreateTile(name2p, tiledata, palettedata);
        }

        public Tile.RenderTile GetTileByName(string name)
        {
            var b = namedTile.TryGetValue(name, out Tile.RenderTile tile);
            if (!b)
            {
                var bdata = namedTileData.TryGetValue(name, out TileData data);
                if (bdata)
                {
                    tile = CreateTile(name, data);
                }
            }
            return tile;
        }
    }

}
